/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "i2c.h"

using namespace dev;

uint32_t i2c_speed_map[] = {
    100, // 0 -> 100K
    400, // 1 -> 400K
    3400 // 2 -> 3400K
};

uint32_t bus_i2c::get_i2c_speed(uint32_t speed) {
    // 确保speed在有效范围内，避免数组越界
    constexpr size_t speed_map_size = sizeof(i2c_speed_map) / sizeof(i2c_speed_map[0]);
    if (speed >= speed_map_size) {
        return speed;
    }
    return i2c_speed_map[speed];
}

bool bus_i2c::init(mc::mutable_dict& csr_object) {
    try {
        // 从字典中提取各个属性值并设置到对象中
        if (csr_object.contains("Id")) {
            this->m_id = csr_object["Id"].as<uint8_t>();
        }
        if (csr_object.contains("Speed")) {
            this->m_speed = get_i2c_speed(csr_object["Speed"].as<uint32_t>());
        }
        if (csr_object.contains("Mode")) {
            this->m_mode = csr_object["Mode"].as<uint8_t>();
        }
        if (csr_object.contains("SlaveAddr")) {
            this->m_slave_addr = csr_object["SlaveAddr"].as<uint8_t>();
        }
        if (csr_object.contains("UseSmbus")) {
            this->m_use_smbus = csr_object["UseSmbus"].as<bool>();
        }
        this->m_i2c_drv = std::make_unique<DRIVER_I2C::I2c>();
        if (m_i2c_drv) {
            m_i2c_drv->init(this->m_id, this->m_speed, this->m_mode, this->m_slave_addr);
        }
    } catch (const std::exception& e) {
        elog("I2c init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }

    return true;
}

uint8_t bus_i2c::get_id() {
    return this->m_id;
}

void bus_i2c::bus_lock() {
    this->m_i2c_drv->lock();
}

void bus_i2c::bus_unlock() {
    this->m_i2c_drv->unlock();
}

std::string_view bus_i2c::format_tx_buf(const ACCESS_DATA_S& data) {
    // 验证偏移量宽度
    if (data.offset_width > MAX_OFFSET_WIDTH) {
        MC_THROW(mc::invalid_arg_exception, "offsetWidth must be 0~4");
    }

    // 清空并重新使用成员变量缓冲区
    m_tx_buffer.clear();

    // 计算总长度：地址(1字节) + 偏移量(offset_width字节) + 数据长度
    size_t total_length = 1 + data.offset_width + data.length;
    m_tx_buffer.reserve(total_length);

    // 添加地址字节
    m_tx_buffer.push_back(static_cast<char>(data.addr & 0xFF));

    // 添加偏移量字节
    for (size_t i = 0; i < data.offset_width; ++i) {
        m_tx_buffer.push_back(
            static_cast<char>((data.offset >> ((data.offset_width - 1 - i) * 8)) & 0xFF));
    }

    // 添加数据缓冲区内容
    if (!data.in_buffer.empty() && data.length > 0) {
        m_tx_buffer.append(reinterpret_cast<const char*>(data.in_buffer.data()), data.length);
    }

    return std::string_view(m_tx_buffer);
}

uint32_t bus_i2c::normal_read(ACCESS_DATA_S& data) {
    // 格式化发送缓冲区
    const std::string_view tx_buf = format_tx_buf(data);

    // 准备读取参数
    DRIVER_I2C::I2C_READ_S read_data{};
    read_data.rv_id        = this->get_id();
    read_data.length       = data.length;
    read_data.time_out_cnt = data.read_time_out > 0 ? data.read_time_out : DEFAULT_TIMEOUT_MS;
    read_data.re_read_cnt  = 1;

    // 声明变量
    int                        ret = 0;
    std::optional<std::string> opt_str;

    // 执行读取操作
    try {
        std::tie(ret, opt_str) = this->m_i2c_drv->read(&read_data, tx_buf);
    } catch (const std::exception& e) {
        MC_THROW(mc::invalid_arg_exception, "I2c read failed, exception: ${exception}",
                 ("exception", e.what()));
    }

    if (ret != 0) {
        return ret;
    }

    // 处理读取结果
    if (opt_str.has_value()) {
        const std::string& str = opt_str.value();
        data.out_buffer.clear();
        data.out_buffer.assign(str.begin(), str.end());
    } else {
        data.out_buffer.clear();
    }

    return ret;
}

uint32_t bus_i2c::extend_read(ACCESS_DATA_S& data) {
    // 清空并重新使用成员变量缓冲区
    m_tx_buffer.clear();

    // 计算总长度：地址(1字节) + 数据长度
    size_t total_length = 1 + data.in_buffer.size();
    m_tx_buffer.reserve(total_length);

    // 添加地址字节
    m_tx_buffer.push_back(static_cast<char>(data.addr & 0xFF));

    // 添加数据缓冲区内容, 转换为string_view
    m_tx_buffer.append(reinterpret_cast<const char*>(data.in_buffer.data()), data.in_buffer.size());
    std::string_view tx_buf = std::string_view(m_tx_buffer);

    // 准备读取参数, 读取重试3次
    DRIVER_I2C::I2C_READ_S read_data{};
    read_data.rv_id        = this->get_id();
    read_data.length       = data.length;
    read_data.time_out_cnt = data.read_time_out > 0 ? data.read_time_out : DEFAULT_TIMEOUT_MS;
    read_data.re_read_cnt  = 3;

    // 声明变量
    int                        ret = 0;
    std::optional<std::string> opt_str;

    // 执行读取操作
    try {
        std::tie(ret, opt_str) = this->m_i2c_drv->read(&read_data, tx_buf);
    } catch (const std::exception& e) {
        MC_THROW(mc::invalid_arg_exception, "I2c read failed, exception: ${exception}",
                 ("exception", e.what()));
    }

    if (ret != 0) {
        return ret;
    }

    // 处理读取结果
    if (opt_str.has_value()) {
        const std::string& str = opt_str.value();
        data.out_buffer.clear();
        data.out_buffer.assign(str.begin(), str.end());
    } else {
        data.out_buffer.clear();
    }

    return ret;
}

uint32_t bus_i2c::read(ACCESS_DATA_S& data) {
    if (data.offset == I2C_EXTEND_READ_MODE) {
        return extend_read(data);
    }
    return normal_read(data);
}

uint32_t bus_i2c::write(ACCESS_DATA_S& data) {
    // 验证写入长度
    if (data.length > MAX_WRITE_LEN) {
        MC_THROW(mc::invalid_arg_exception, "write error, input len too long");
    }

    // 准备写入数据
    const std::string_view tx_buf = format_tx_buf(data);

    try {
        // 执行写入操作
        const auto ret = this->m_i2c_drv->write(this->m_id, tx_buf, DEFAULT_TIMEOUT_MS, 0);
        return ret;
    } catch (const std::exception& e) {
        MC_THROW(mc::invalid_arg_exception, "I2c write failed, exception: ${exception}",
                 ("exception", e.what()));
    }
}